#include "SpaceInvadersPCH.h"
#include "RenderDataProcessor.h"
#include "AutoGen_RenderData.h"
#include "Material.h"
#include "Pool.h"

namespace ObjectPools
{
	Pool<gk::StreamDescription, 4096>	g_poolStreamDescriptions;
	Pool<gk::Texture, 4096>				g_poolTextures;
	Pool<gk::Buffer, 4096>				g_poolBuffers;
	Pool<gk::GpuProgram, 4096>			g_poolGpuPrograms;
	Pool<gk::RenderBinding, 4096>		g_poolRenderBindings;
	Pool<Material, 4096>				g_poolMaterials;
}

void RenderDataProcessor::DataProcessor_Register(data::DataBlockHeader* pHeader, void* pData)
{
	switch (pHeader->GetId())	
	{
	case shared::prehash::hs_RDAT:
		{
			Data_RenderData::RenderData* pRenderData = static_cast<Data_RenderData::RenderData*>(pData);
			pRenderData->Fixup();
			for (cr::uint32_t i = 0; i < pRenderData->m_numBufferDeclarations; ++i)
			{
				Data_RenderData::BufferDeclaration& bufferDecl = pRenderData->m_pBufferDeclarations[i];
				gk::StreamDescriptionHandle hStreamDescription = ObjectPools::g_poolStreamDescriptions.Allocate();
				for (cr::uint32_t iElement = 0; iElement < bufferDecl.m_numElements; ++iElement)
				{
					const Data_RenderData::BufferDeclarationElement& element = pRenderData->m_pBufferDeclarationElements[bufferDecl.m_idxFirstElement+iElement];
					hStreamDescription->m_elements[iElement].m_hsName = element.m_name;
					hStreamDescription->m_elements[iElement].m_hsSemantic = element.m_semantic;
					hStreamDescription->m_elements[iElement].m_hsType = element.m_type;
					hStreamDescription->m_elements[iElement].m_offset = element.m_offset;
					hStreamDescription->m_elements[iElement].m_name[0] = '\0';
				}
				hStreamDescription->m_numElements = bufferDecl.m_numElements;
				hStreamDescription->m_stride = bufferDecl.m_stride;
				gk::GetDevice()->CreateStreamDescription(hStreamDescription);
				bufferDecl.m_runtimeData = (cr::uint32_t)hStreamDescription.GetPtr();
			}

			for (cr::uint32_t i = 0; i < pRenderData->m_numTextures; ++i)
			{
				Data_RenderData::Texture& srcTexture = pRenderData->m_pTextures[i];
				gk::TextureHandle hDstTexture = ObjectPools::g_poolTextures.Allocate();
				hDstTexture->m_initDataSize = srcTexture.m_numInitDataBytes;
				hDstTexture->m_pInitData = (void*)(pRenderData->m_pRawBytes + srcTexture.m_idxInitData);
				gk::GetDevice()->CreateTexture(hDstTexture);
				srcTexture.m_runtimeData = (cr::uint32_t)hDstTexture.GetPtr();
			}

			for (cr::uint32_t i = 0; i < pRenderData->m_numBuffers; ++i)
			{
				Data_RenderData::Buffer& srcBuffer = pRenderData->m_pBuffers[i];
				gk::BufferHandle hDstBuffer = ObjectPools::g_poolBuffers.Allocate();
				
				hDstBuffer->m_format = (gk::BufferFormat::Enum)srcBuffer.m_format;
				hDstBuffer->m_initDataSize = srcBuffer.m_numInitDataBytes;
				hDstBuffer->m_numElements = srcBuffer.m_numElements;
				hDstBuffer->m_pInitData = &pRenderData->m_pRawBytes[srcBuffer.m_idxInitData];
				hDstBuffer->m_hStreamDescription = (gk::StreamDescription*)(pRenderData->m_pBufferDeclarations[srcBuffer.m_idxBufferDeclaration].m_runtimeData);
				if (hDstBuffer->m_hStreamDescription.IsValid())
					hDstBuffer->m_stride = hDstBuffer->m_hStreamDescription->m_stride;
				else
				{
					hDstBuffer->m_stride = hDstBuffer->m_format == Data_RenderData::EBufferFormat_Index16Stream ? 2 : 4;
				}

				gk::GetDevice()->CreateBuffer(hDstBuffer);
				srcBuffer.m_runtimeData = (cr::uint32_t)hDstBuffer.GetPtr();
			}

			for (cr::uint32_t i = 0; i < pRenderData->m_numGpuPrograms; ++i)
			{
				Data_RenderData::GpuProgram& srcGpuProgram = pRenderData->m_pGpuPrograms[i];

				gk::GpuProgramHandle hGpuProgram = ObjectPools::g_poolGpuPrograms.Allocate();
				hGpuProgram->m_initDataSize = srcGpuProgram.m_numInitDataBytes;
				hGpuProgram->m_pInitData = (void*)(pRenderData->m_pRawBytes + srcGpuProgram.m_idxInitDataBytes);
				gk::GetDevice()->CreateGpuProgram(hGpuProgram);
				srcGpuProgram.m_runtimeData = (cr::uint32_t)hGpuProgram.GetPtr();
			}

			for (cr::uint32_t i = 0; i < pRenderData->m_numMaterials; ++i)
			{
				Data_RenderData::Material& srcMaterial = pRenderData->m_pMaterials[i];

				Material* pMaterial = ObjectPools::g_poolMaterials.Allocate();

				for (cr::uint32_t iRM = 0; iRM < srcMaterial.m_numRenderMethods; ++iRM)
				{
					Data_RenderData::RenderMethod& srcRenderMethod = pRenderData->m_pRenderMethods[i];
					RenderMethod& renderMethod = pMaterial->CreateRenderMethod("Default", (gk::GpuProgram*)((pRenderData->m_pGpuPrograms + srcRenderMethod.m_idxGpuProgram)->m_runtimeData));

					for (cr::uint32_t j = 0; j < srcMaterial.m_numMaterialParams; ++j)
					{
						renderMethod.SetParam(pRenderData, srcMaterial.m_idxFirstMaterialParam+j);
					}

					for (cr::uint32_t j = 0; j < srcRenderMethod.m_numParameters; ++j)
					{
						renderMethod.SetParam(pRenderData, srcRenderMethod.m_idxFirstParameter+j);
					}
				}

				srcMaterial.m_runtimeData = (cr::uint32_t)pMaterial;
			}

			for (cr::uint32_t i = 0; i < pRenderData->m_numSubmodels; ++i)
			{
				Data_RenderData::Submodel& srcSubmodel = pRenderData->m_pSubmodels[i];
				Data_RenderData::Material& srcMaterial = pRenderData->m_pMaterials[srcSubmodel.m_idxMaterial];
				Data_RenderData::RenderMethod& srcRenderMethod = pRenderData->m_pRenderMethods[srcMaterial.m_idxFirstRenderMethod];
				
				gk::RenderBindingHandle hRenderBinding = ObjectPools::g_poolRenderBindings.Allocate();
				hRenderBinding->m_vertexBuffer[0] = (gk::Buffer*)pRenderData->m_pBuffers[srcSubmodel.m_idxVertexBuffer].m_runtimeData;
				hRenderBinding->m_hGpuProgram = (gk::GpuProgram*)((pRenderData->m_pGpuPrograms + srcRenderMethod.m_idxGpuProgram)->m_runtimeData);
				gk::GetDevice()->CreateRenderBinding(hRenderBinding);
				srcSubmodel.m_runtimeData = (cr::uint32_t)hRenderBinding.GetPtr();
			}

			break;
		}
	}

	m_unregistering = false;
}

void RenderDataProcessor::DataProcessor_Unregister(data::DataBlockHeader* pHeader, void* pData)
{
	m_unregisterWaitFrames = 10;
	m_pHeader = pHeader;
	m_pData = pData;
	m_unregistering = true;
}

bool RenderDataProcessor::DataProcessor_AreYouDoneYet()
{
	bool finished = true;

	if (m_unregistering)
	{
		finished = false;
		if (--m_unregisterWaitFrames == 0)
		{
			switch (m_pHeader->GetId())	
			{
			case shared::prehash::hs_RDAT:
				{
					Data_RenderData::RenderData* pRenderData = (Data_RenderData::RenderData*)m_pData;

					for (cr::uint32_t i = 0; i < pRenderData->m_numSubmodels; ++i)
					{
						Data_RenderData::Submodel& srcObj = pRenderData->m_pSubmodels[i];
						gk::RenderBindingHandle hObj = (gk::RenderBinding*)srcObj.m_runtimeData;
						gk::GetDevice()->ReleaseRenderBinding(hObj);
						ObjectPools::g_poolRenderBindings.Free(hObj.GetPtr());
						srcObj.m_runtimeData = 0;
					}

					for (cr::uint32_t i = 0; i < pRenderData->m_numMaterials; ++i)
					{
						Data_RenderData::Material& srcObj = pRenderData->m_pMaterials[i];
						Material* pMaterial = (Material*)srcObj.m_runtimeData;
						pMaterial->ReleaseRenderMethods();
						ObjectPools::g_poolMaterials.Free(pMaterial);
						srcObj.m_runtimeData = 0;
					}

					for (cr::uint32_t i = 0; i < pRenderData->m_numGpuPrograms; ++i)
					{
						Data_RenderData::GpuProgram& srcObj = pRenderData->m_pGpuPrograms[i];
						gk::GpuProgramHandle hObj = (gk::GpuProgram*)srcObj.m_runtimeData;
						gk::GetDevice()->ReleaseGpuProgram(hObj);
						ObjectPools::g_poolGpuPrograms.Free(hObj.GetPtr());
						srcObj.m_runtimeData = 0;
					}

					for (cr::uint32_t i = 0; i < pRenderData->m_numBuffers; ++i)
					{
						Data_RenderData::Buffer& srcObj = pRenderData->m_pBuffers[i];
						gk::BufferHandle hObj = (gk::Buffer*)srcObj.m_runtimeData;
						gk::GetDevice()->ReleaseBuffer(hObj);
						ObjectPools::g_poolBuffers.Free(hObj.GetPtr());
						srcObj.m_runtimeData = 0;
					}

					for (cr::uint32_t i = 0; i < pRenderData->m_numTextures; ++i)
					{
						Data_RenderData::Texture& srcObj = pRenderData->m_pTextures[i];
						gk::TextureHandle hObj = (gk::Texture*)srcObj.m_runtimeData;
						gk::GetDevice()->ReleaseTexture(hObj);
						ObjectPools::g_poolTextures.Free(hObj.GetPtr());
						srcObj.m_runtimeData = 0;
					}

					for (cr::uint32_t i = 0; i < pRenderData->m_numBufferDeclarations; ++i)
					{
						Data_RenderData::BufferDeclaration& srcObj = pRenderData->m_pBufferDeclarations[i];
						gk::StreamDescriptionHandle hObj = (gk::StreamDescription*)srcObj.m_runtimeData;
						gk::GetDevice()->ReleaseStreamDescription(hObj);
						ObjectPools::g_poolStreamDescriptions.Free(hObj.GetPtr());
						srcObj.m_runtimeData = 0;
					}

					break;
				}
			}

			finished = true;
			m_pHeader = 0;
			m_pData = 0;
		}
	}
	
	return finished;
}